﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using chromium.Log;

namespace chromium.Cert
{
    //1. makecert -r -pe -n "CN=Test Certificate" -sky exchange -sv testcert.pvk testcert.cer
    //2. pvk2pfx -pvk testcert.pvk -spc testcert.cer -pfx testcert.pfx
    public static class SSPICertUtils
    {
        private static readonly ILog logger = LogManager.GetLogger(typeof(SSPICertUtils));
        private static readonly string certFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cert/");
        private static readonly Dictionary<string, X509Certificate> certCache = new Dictionary<string, X509Certificate>();

        public static SslStream WrapStream(string host, Stream networkStream)
        {
            X509Certificate certificate = GetCert(NormalizeHost(host));

            var sslStream = new SslStream(networkStream, false);

            sslStream.AuthenticateAsServer(certificate, false, SslProtocols.Tls, false);

            return sslStream;
        }

        public static X509Certificate GetCert(string host)
        {
            if (!File.Exists(Path.Combine(certFolder, "_ca.pfx")))
            {
                MakeCA();
            }

            string certPath = Path.Combine(certFolder, host.Substring(2) + ".pfx");

            if (!certCache.ContainsKey(certPath))
            {
                lock (certCache)
                {
                    if (!certCache.ContainsKey(certPath))
                    {
                        if (!File.Exists(certPath))
                        {
                            MakeCert(host);
                        }
                        certCache.Add(certPath, new X509Certificate2(certPath));
                    }
                }
            }

            return certCache[certPath];
        }

        public static string NormalizeHost(string host)
        {
            int x = host.LastIndexOf(":");
            if (x > -1)
                host = host.Substring(0, x);

            int pos = host.IndexOf('.');
            int pos1 = host.LastIndexOf('.');

            if (pos != pos1 && pos != -1)
                return "*" + host.Substring(pos);

            return host;
        }

        private static void MakeCert(string host)
        {
            string fileName = host.Substring(2);
            string capvk = Path.Combine(certFolder, "_ca.pvk");
            string cacer = Path.Combine(certFolder, "_ca.cer");
            string pvk = Path.Combine(certFolder, fileName + ".pvk");
            string cer = Path.Combine(certFolder, fileName + ".cer");
            string pfx = Path.Combine(certFolder, fileName + ".pfx");
            var makeCertProcessInfo = new ProcessStartInfo("makecert.exe")
                                          {
                                              Arguments = string.Format("-iv \"{0}\" -ic \"{1}\" -pe -n \"CN={2}\" -sky exchange -sv \"{3}\" \"{4}\"", capvk, cacer, host, pvk, cer),
                                              UseShellExecute = false,
                                              CreateNoWindow = true,
                                              RedirectStandardOutput = true
                                          };

            Process makeCertProcess = Process.Start(makeCertProcessInfo);

            //SetNoPass();

            logger.Debug(makeCertProcess.StandardOutput.ReadToEnd());

            makeCertProcess.WaitForExit();

            var ps2 = new ProcessStartInfo("pvk2pfx.exe")
                          {
                              Arguments = string.Format("-pvk \"{0}\" -spc \"{1}\" -pfx \"{2}\"", pvk, cer, pfx),
                              UseShellExecute = false,
                              CreateNoWindow = true,
                              RedirectStandardOutput = true
                          };

            Process p2 = Process.Start(ps2);

            logger.Debug(p2.StandardOutput.ReadToEnd());

            p2.WaitForExit();

            File.Delete(cer);
            File.Delete(pvk);
        }

        private static void MakeCA()
        {
            string capvk = Path.Combine(certFolder, "_ca.pvk");
            string cacer = Path.Combine(certFolder, "_ca.cer");
            string capfx = Path.Combine(certFolder, "_ca.pfx");
            var makeCertProcessInfo = new ProcessStartInfo("makecert.exe")
                                          {
                                              Arguments = string.Format("-r -pe -n \"CN=SSPI Proxy Root Certificate\" -sky exchange -sv \"{0}\" \"{1}\"", capvk, cacer),
                                              UseShellExecute = false,
                                              CreateNoWindow = true,
                                              RedirectStandardOutput = true
                                          };

            Process p = Process.Start(makeCertProcessInfo);

            //SetNoPass();

            logger.Debug(p.StandardOutput.ReadToEnd());

            p.WaitForExit();

            var ps2 = new ProcessStartInfo("pvk2pfx.exe")
                          {
                              Arguments = string.Format("-pvk \"{0}\" -spc \"{1}\" -pfx \"{2}\"", capvk, cacer, capfx),
                              UseShellExecute = false,
                              CreateNoWindow = true,
                              RedirectStandardOutput = true
                          };

            Process p2 = Process.Start(ps2);

            logger.Debug(p2.StandardOutput.ReadToEnd());

            p2.WaitForExit();
        }
    }
}